/**
 * Copyright 2008 Adam Ruggles.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package kiff.util.jpa;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;

import kiff.util.ContextManager;
import kiff.util.transaction.Transaction;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.inject.Inject;

/**
 * Entity Manager Proxy.
 * @author Adam
 * @version $Id: EntityManagerProxy.java 62 2008-10-31 04:51:50Z a.ruggles $
 * 
 * Created on Oct 8, 2008 at 9:28:30 PM 
 */
public class EntityManagerProxy implements InvocationHandler {
	/**
     * The Entity Manager Factory.
     */
    private EntityManagerFactory entityManagerFactory;

    /**
     * The <code>Logger</code> is used by the application to generate a log messages.
     */
    private final Logger logger = LoggerFactory.getLogger(EntityManagerProxy.class);

    /**
     * Constructs a Entity Manager Proxy.
     * @param entityManagerFactory The Entity Manager Factory.
     */
    @Inject
    public EntityManagerProxy(final EntityManagerFactory entityManagerFactory) {
    	this.entityManagerFactory = entityManagerFactory;
    }

	/**
	 * {@inheritDoc}
	 * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
	 */
	public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
		Object result;
		try {
			if (method.getName().equals("close")) {
				if (ContextManager.hasBind(Transaction.class)) {
					Transaction tx = ContextManager.unbind(Transaction.class);
					logger.debug("Transaction [{}] unbound.", tx);
				}
				if (ContextManager.hasBind(EntityManager.class)) {
					EntityManager em = ContextManager.unbind(EntityManager.class);
					logger.debug("Entity Manager [{}] unbound.", em);
					if (em != null && em.isOpen()) {
						logger.debug("Closing Entity Manager {}", em);
						em.close();
						return null;
					}
				}
				logger.warn("[close] was called on a closed or empty Entity Manager");
				return null;
			}
			EntityManager entityManager = ContextManager.getBind(EntityManager.class);
			if (entityManager == null) {
				entityManager = entityManagerFactory.createEntityManager();
				logger.debug("Binding new Entity Manager [{}]", entityManager);
				ContextManager.bind(EntityManager.class, entityManager);
			}
			logger.debug("invoking [{}] on the EntityManager [{}]", method.getName(), entityManager);
			result = method.invoke(entityManager, args);
		} catch (InvocationTargetException e) {
			throw e.getTargetException();
		} catch (Exception e) {
			throw e;
		}
		return result;
	}

}
